﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace Notoric.Gui.Controls.Adorners
{
    /// <summary>
    /// Implements the visual tree of the <see cref="RRMAdorner"/>.
    /// </summary>
    /// <remarks>
    /// In order for this control to work correctly, its <see cref="FrameworkElement.DataContext"/> has to be set
    /// to the manipulated <see cref="FrameworkElement"/> that is placed on a <see cref="Canvas"/>.
    /// </remarks>
    public partial class RRMChrome : UserControl
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="RRMChrome"/> instance.
        /// </summary>
        public RRMChrome()
        {
            InitializeComponent();
        }

        #endregion

        #region Routed Events Fields

        /// <summary>
        /// Identifies the <see cref="MoveCompleted"/> event.
        /// </summary>
        public static readonly RoutedEvent MoveCompletedEvent = EventManager.RegisterRoutedEvent(
            "MoveCompleted", RoutingStrategy.Bubble, typeof(EventHandler<MoveCompletedEventArgs>), typeof(RRMChrome));

        /// <summary>
        /// Identifies the <see cref="ResizeCompleted"/> event.
        /// </summary>
        public static readonly RoutedEvent ResizeCompletedEvent = EventManager.RegisterRoutedEvent(
            "ResizeCompleted", RoutingStrategy.Bubble, typeof(EventHandler<ResizeCompletedEventArgs>), typeof(RRMChrome));

        /// <summary>
        /// Identifies the <see cref="RotateCompleted"/> event.
        /// </summary>
        public static readonly RoutedEvent RotateCompletedEvent = EventManager.RegisterRoutedEvent(
            "RotateCompleted", RoutingStrategy.Bubble, typeof(EventHandler<RotateCompletedEventArgs>), typeof(RRMChrome));

        #endregion

        #region Events

        /// <summary>
        /// Occurs when the user finished moving the manipulatied object.
        /// </summary>
        public event EventHandler<MoveCompletedEventArgs> MoveCompleted
        {
            add { AddHandler(MoveCompletedEvent, value); }
            remove { RemoveHandler(MoveCompletedEvent, value); }
        }

        /// <summary>
        /// Occurs when the user finished resizing the manipulated object.
        /// </summary>
        public event EventHandler<ResizeCompletedEventArgs> ResizeCompleted
        {
            add { AddHandler(ResizeCompletedEvent, value); }
            remove { RemoveHandler(ResizeCompletedEvent, value); }
        }

        /// <summary>
        /// Occurs when the user finished rotating the manipulated object.
        /// </summary>
        public event EventHandler<RotateCompletedEventArgs> RotateCompleted
        {
            add { AddHandler(RotateCompletedEvent, value); }
            remove { RemoveHandler(RotateCompletedEvent, value); }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the <see cref="MoveCompleted"/> routed event on this <see cref="RRMChrome"/> instance.
        /// </summary>
        /// <param name="left">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The distance, in device independent pixels, between the left edge of the manipulate object
        /// and the left edge of its container's area.
        /// </para>
        /// </param>
        /// <param name="top">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The distance, in device independent pixels, between the top edge of the manipulate object
        /// and the top edge of its container's area.
        /// </para>
        /// </param>
        protected void RaiseMoveCompleted(double left, double top)
        {
            var args = new MoveCompletedEventArgs(left, top);
            RaiseEvent(args);
        }

        /// <summary>
        /// Raises the <see cref="ResizeCompleted"/> routed event on this <see cref="RRMChrome"/> instance.
        /// </summary>
        /// <param name="width">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// Final width, in device independent pixels, of the manipulated object
        /// </para>
        /// </param>
        /// <param name="height">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// Final height, in device independent pixels, of the manipulated object
        /// </para>
        /// </param>
        protected void RaiseResizeCompleted(double width, double height)
        {
            var args = new ResizeCompletedEventArgs(width, height);
            RaiseEvent(args);
        }

        /// <summary>
        /// Raises the <see cref="RotateCompleted"/> routed event on this <see cref="RRMChrome"/> instance.
        /// </summary>
        /// <param name="rotationAngle">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// Final rotation angle, in degrees, of a clockwise rotation of the manipulated object.
        /// </para>
        /// </param>
        protected void RaiseRotateCompleted(double rotationAngle)
        {
            var args = new RotateCompletedEventArgs(rotationAngle);
            RaiseEvent(args);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the user dropped the move thumb (i.e. finished moving the manipulated object).
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler raises the <see cref="MoveCompleted"/> routed event and passes it the final position
        /// that is set to the manipulated object.
        /// </remarks>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void MoveThumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            var movedItem = DataContext as FrameworkElement;

            if (movedItem != null)
                RaiseMoveCompleted(Canvas.GetLeft(movedItem), Canvas.GetTop(movedItem));
        }

        /// <summary>
        /// Occurs when the user dropped the rotation thumb (i.e. finished rotating the manipulated object).
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler raises the <see cref="RotateCompleted"/> routed event and passes it the final rotation angle
        /// that is set to the rotation transformation of the manipulated object.
        /// </remarks>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void RotateThumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            var rotatedItem = DataContext as FrameworkElement;

            if (rotatedItem != null)
            {
                var transform = rotatedItem.RenderTransform as RotateTransform;
                if (transform != null)
                    RaiseRotateCompleted(transform.Angle);
            }
        }

        /// <summary>
        /// Occurs when the user dropped a resize thumb (i.e. finished resizing the manipulated object).
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler raises the <see cref="ResizeCompleted"/> routed event and passes it the final size of the manipulated object.
        /// </remarks>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void ResizeThumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            var resizedItem = DataContext as FrameworkElement;

            if (resizedItem != null)
                RaiseResizeCompleted(resizedItem.Width, resizedItem.Height);
        }

        #endregion
    }

    /// <summary>
    /// Provides data for the <see cref="RRMChrome.MoveCompleted"/> event.
    /// </summary>
    public class MoveCompletedEventArgs : RoutedEventArgs
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="MoveCompletedEventArgs"/> instance.
        /// </summary>
        /// <remarks>
        /// The <see cref="RoutedEventArgs.RoutedEvent"/> field is automatically set to <see cref="RRMChrome.MoveCompleted"/>.
        /// </remarks>
        /// <param name="left">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The distance, in device independent pixels, between the left edge
        /// of the control and the left edge of its container's client area.
        /// </para>
        /// </param>
        /// <param name="top">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The distance, in device independent pixels, between the top edge
        /// of the control and the top edge of its container's client area.
        /// </para>
        /// </param>
        public MoveCompletedEventArgs(double left, double top)
            : base(RRMChrome.MoveCompletedEvent)
        {
            Left = left;
            Top = top;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the distance between the left edge of the control 
        /// and the left edge of its container's client area.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The distance, in device independent pixels, between the left edge
        /// of the control and the left edge of its container's client area.
        /// </para>
        /// </value>
        public double Left
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the distance between the top edge of the control 
        /// and the top edge of its container's client area.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The distance, in device independent pixels, between the top edge
        /// of the control and the top edge of its container's client area.
        /// </para>
        /// </value>
        public double Top
        {
            get;
            set;
        }
        
        #endregion
    }

    /// <summary>
    /// Provides data for the <see cref="RRMChrome.ResizeCompleted"/> event.
    /// </summary>
    public class ResizeCompletedEventArgs : RoutedEventArgs
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="ResizeCompletedEventArgs"/> instance.
        /// </summary>
        /// <remarks>
        /// The <see cref="RoutedEventArgs.RoutedEvent"/> field is automatically set to <see cref="RRMChrome.ResizeCompleted"/>.
        /// </remarks>
        /// <param name="width">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// Final width of the manipulated object
        /// </para>
        /// </param>
        /// <param name="height">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// Final height of the manipulated object
        /// </para>
        /// </param>
        public ResizeCompletedEventArgs(double width, double height)
            : base(RRMChrome.ResizeCompletedEvent)
        {
            Width = width;
            Height = height;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the final height of the manipulated object.
        /// </summary>
        /// <value>
        /// The height of the object in device independent units.
        /// </value>
        public double Height
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the final width of the manipulated object.
        /// </summary>
        /// <value>
        /// The width of the object in device independent units.
        /// </value>
        public double Width
        {
            get;
            set;
        }

        #endregion
    }

    /// <summary>
    /// Provides data for the <see cref="RRMChrome.RotateCompleted"/> event.
    /// </summary>
    public class RotateCompletedEventArgs : RoutedEventArgs
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="RotateCompletedEventArgs"/> instance.
        /// </summary>
        /// <remarks>
        /// The <see cref="RoutedEventArgs.RoutedEvent"/> field is automatically set to <see cref="RRMChrome.RotateCompleted"/>.
        /// </remarks>
        /// <param name="rotationAngle">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The angle, in degrees, of a clock-wise rotation.
        /// </para>
        /// </param>
        public RotateCompletedEventArgs(double rotationAngle)
            : base(RRMChrome.RotateCompletedEvent)
        {
            RotationAngle = rotationAngle;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the final rotation angle of the manipulated object.
        /// </summary>
        /// <value>
        /// The angle, in degrees, of a clockwise rotation.
        /// </value>
        public double RotationAngle
        {
            get;
            set;
        }

        #endregion
    }
}
